#!/usr/bin/python
#-*- coding:utf-8 -*-

import patterns
import sys
import argparse
from matplotlib import pyplot as plt
from matplotlib import animation
import numpy as np
from scipy import signal


def generate_world(opts):
    world = []

    ON = 1
    OFF = 0
    vals = [ON, OFF]
    if opts.world_type=='empty':
        world = np.zeros((opts.cols,opts.rows))
    elif opts.world_type=='random':
        world = np.random.choice(vals, size=(opts.cols,opts.rows), p=[0.2, 0.8])

    return world


def update_frame(frame_num, opts, world, img):
    """
    Accepts: frame_num  -- (automatically passed in) current frame number
             opts       -- a populated command line options instance
             world      -- the 2D world pixel buffer
             img        -- the plot image
    """

    img.set_array(world)

    new_world = []
    for row in world:
        new_world.append(row[:])

    fliter0 = np.ones((3,3))
    fliter0[1,1] = 0    #构造卷积核
    new_world1 = signal.convolve2d(world, fliter0,'same')   #使用scipy实现原矩阵的same卷积
    world2 = new_world1.copy()
    world3 = new_world1.copy()
    world2[world2!=2] = 0
    world[world2==0] = 0
    world2[world==0] = 0
    world3[world3!=3] = 0
    world4 = world2+world3
    world4[world4!=0] = 1
    new_world = []
    for row in world4:
        new_world.append(row[:])
    
    world[:] = new_world[:]
    return img,


def blit(world, sprite, x, y):
    """
    Accepts: world  -- a 2D world pixel buffer generated by generate_world()
             sprite -- a 2D matrix containing a pattern of 1s and 0s
             x      -- x world coord where left edge of sprite will be placed
             y      -- y world coord where top edge of sprite will be placed

    Returns: (Nothing)

    Description: Copies a 2D pixel pattern (i.e sprite) into the larger 2D
                 world.  The sprite will be copied into the 2D world with
                 its top left corner being located at world coordinate (x,y)
    """
    for i in range(len(sprite)):
        for j in range(len(sprite[0])):
            world[i+x,j+y]=sprite[i][j]


def run_simulation(opts, world):
    """
    Accepts: opts  -- a populated command line options class instance
             world -- a 2D world pixel buffer generated by generate_world()

    Returns: (Nothing)

    Description: This function generates the plot that we will use as a
                 rendering surfance.  'Living' cells (represented as 1s in
                 the 2D world matrix) will be rendered as black pixels and
                 'dead' cells (represetned as 0s) will be rendered as
                 white pixels.  The method FuncAnimation() accepts 4
                 parameters: the figure, the frame update function, a
                 tuple containing arguments to pass to the update function,
                 and the frame update interval (in milliseconds).  Once the
                 show() method is called to display the plot, the frame
                 update function will be called every 'interval'
                 milliseconds to update the plot image (img).
    """
    if world == []:
        print ("The 'world' was never created.  Exiting")
        sys.exit()

    fig = plt.figure()
    img = plt.imshow(world, interpolation='none', cmap='Greys', vmax=1, vmin=0)
    ani = animation.FuncAnimation(fig,
                                  update_frame,
                                  fargs=(opts, world, img),
                                  interval=opts.framedelay)

    plt.show()


def report_options(opts):
    print ("Conway's Game of Life")
    print ("=====================")
    print ("   World Size: %i x %i" % (opts.rows, opts.cols))
    print ("   World Type: %s" % (opts.world_type))
    print ("  Frame Delay: %i (ms)" % (opts.framedelay))


def get_commandline_options():
    parser = argparse.ArgumentParser()

    parser.add_argument('-r', '--rows',
                        help='set # of rows in the world',
                        action='store',
                        type=int,
                        dest='rows',
                        default=50)


    # Add '-c', '--columns' here. Default is the number of rows.
    parser.add_argument('-c', '--columns',
                        help='set # of columns in the world',
                        action='store',
                        type=int,
                        dest='cols',
                        default=50)

    # Add '-w', '--world' here. Destination is dest='world_type', default is 'empty'
    parser.add_argument('-w', '--world',
                        help='set # of type in the world',
                        action='store',
                        type=str,
                        dest='world_type',
                        default='empty')

    # set the --framedelay here. Name it -d. Default value is 100
    parser.add_argument('-d',
                        help='set # of framedelay to generate',
                        action='store',
                        type=int,
                        dest='framedelay',
                        default=100)

    # add two more optional command line arguments here. Be creative!
    parser.add_argument('-sr',
                        help='the right location to generate',
                        action='store',
                        type=int,
                        dest='sr',
                        default=20)
    parser.add_argument('-sl',
                        help='the left location to generate',
                        action='store',
                        type=int,
                        dest='sl',
                        default=20)

    opts = parser.parse_args()

    return opts


def main():
    """
    The main function -- everything starts here!
    """
    opts = get_commandline_options()
    world = generate_world(opts)
    report_options(opts)

    blit(world, patterns.glider, opts.sr, opts.sl)

    run_simulation(opts, world)


if __name__ == '__main__':
    main()
